/*
 * Copyright 2013 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.aplink.generic.google.maps.ui;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.aplink.sg.R;

/**
 * IconGenerator generates icons that contain text (or custom content) within an
 * info window-like shape.
 * <p/>
 * The icon {@link Bitmap}s generated by the factory should be used in
 * conjunction with a
 * {@link com.google.android.gms.maps.model.BitmapDescriptorFactory}.
 * <p/>
 * This class is not thread safe.
 */
public class IconGenerator {
	public static final int STYLE_BLUE = 4;

	public static final int STYLE_DEFAULT = 1;
	public static final int STYLE_GREEN = 5;
	public static final int STYLE_ORANGE = 7;
	public static final int STYLE_PURPLE = 6;

	public static final int STYLE_RED = 3;

	public static final int STYLE_WHITE = 2;

	private static int getBackground(final int style) {
		switch (style) {
			default:
			case STYLE_DEFAULT:
			case STYLE_WHITE:
				return R.drawable.bubble_white;
			case STYLE_RED:
				return R.drawable.bubble_red;
			case STYLE_BLUE:
				return R.drawable.bubble_blue;
			case STYLE_GREEN:
				return R.drawable.bubble_green;
			case STYLE_PURPLE:
				return R.drawable.bubble_purple;
			case STYLE_ORANGE:
				return R.drawable.bubble_orange;
		}
	}

	private static int getTextStyle(final int style) {
		switch (style) {
			default:
			case STYLE_DEFAULT:
			case STYLE_WHITE:
				return R.style.Bubble_TextAppearance_Dark;
			case STYLE_RED:
			case STYLE_BLUE:
			case STYLE_GREEN:
			case STYLE_PURPLE:
			case STYLE_ORANGE:
				return R.style.Bubble_TextAppearance_Light;
		}
	}

	private final float mAnchorU = 0.5f;

	private final float mAnchorV = 1f;

	private ViewGroup mContainer;

	private View mContentView;

	private final Context mContext;

	private int mRotation;

	private RotationLayout mRotationLayout;

	private TextView mTextView;

	/**
	 * Creates a new IconGenerator with the default style.
	 */
	public IconGenerator(final Context context) {
		mContext = context;
	}

	/**
	 * Ensure views are ready. This allows us to lazily inflate the main layout.
	 */
	private void ensureViewsSetUp() {
		if (mContainer == null) {
			mContainer = (ViewGroup) LayoutInflater.from(mContext).inflate(
					R.layout.google__text_bubble, null);
			mRotationLayout = (RotationLayout) mContainer.getChildAt(0);
			mContentView = mTextView = (TextView) mRotationLayout
					.findViewById(R.id.text);
		}
	}

	/**
	 * @return u coordinate of the anchor, with rotation applied.
	 */
	public float getAnchorU() {
		return rotateAnchor(mAnchorU, mAnchorV);
	}

	/**
	 * @return v coordinate of the anchor, with rotation applied.
	 */
	public float getAnchorV() {
		return rotateAnchor(mAnchorV, mAnchorU);
	}

	/**
	 * Not thread safe.
	 */
	private ViewGroup getContainer() {
		ensureViewsSetUp();
		return mContainer;
	}

	/**
	 * Creates an icon with the current content and style.
	 * <p/>
	 * This method is useful if a custom view has previously been set, or if
	 * text content is not applicable.
	 */
	public Bitmap makeIcon() {
		final ViewGroup container = getContainer();

		final int measureSpec = View.MeasureSpec.makeMeasureSpec(0,
				View.MeasureSpec.UNSPECIFIED);
		container.measure(measureSpec, measureSpec);

		int measuredWidth = container.getMeasuredWidth();
		int measuredHeight = container.getMeasuredHeight();

		container.layout(0, 0, measuredWidth, measuredHeight);

		if ((mRotation == 1) || (mRotation == 3)) {
			measuredHeight = container.getMeasuredWidth();
			measuredWidth = container.getMeasuredHeight();
		}

		final Bitmap r = Bitmap.createBitmap(measuredWidth, measuredHeight,
				Bitmap.Config.ARGB_8888);
		r.eraseColor(Color.TRANSPARENT);

		final Canvas canvas = new Canvas(r);

		if (mRotation == 0) {
			// do nothing
		} else if (mRotation == 1) {
			canvas.translate(measuredWidth, 0);
			canvas.rotate(90);
		} else if (mRotation == 2) {
			canvas.rotate(180, measuredWidth / 2, measuredHeight / 2);
		} else {
			canvas.translate(0, measuredHeight);
			canvas.rotate(270);
		}
		container.draw(canvas);
		return r;
	}

	/**
	 * Sets the text content, then creates an icon with the current style.
	 *
	 * @param text
	 *            the text content to display inside the icon.
	 */
	public Bitmap makeIcon(final String text) {
		ensureViewsSetUp();

		if (mTextView != null) {
			mTextView.setText(text);
		}

		return makeIcon();
	}

	/**
	 * Rotates the anchor around (u, v) = (0, 0).
	 */
	private float rotateAnchor(final float u, final float v) {
		switch (mRotation) {
			case 0:
				return u;
			case 1:
				return 1 - v;
			case 2:
				return 1 - u;
			case 3:
				return v;
		}
		throw new IllegalStateException();
	}

	/**
	 * Set the background to a given Drawable, or remove the background.
	 *
	 * @param background
	 *            the Drawable to use as the background, or null to remove the
	 *            background.
	 */
	@SuppressWarnings("deprecation")
	// View#setBackgroundDrawable is compatible with pre-API level 16 (Jelly
	// Bean).
	public void setBackground(final Drawable background) {
		getContainer().setBackgroundDrawable(background);

		// Force setting of padding.
		// setBackgroundDrawable does not call setPadding if the background has
		// 0 padding.
		if (background != null) {
			final Rect rect = new Rect();
			background.getPadding(rect);
			getContainer().setPadding(rect.left, rect.top, rect.right,
					rect.bottom);
		} else {
			getContainer().setPadding(0, 0, 0, 0);
		}
	}

	/**
	 * Sets the padding of the content view. The default padding of the content
	 * view (i.e. text view) is 5dp top/bottom and 10dp left/right.
	 *
	 * @param left
	 *            the left padding in pixels.
	 * @param top
	 *            the top padding in pixels.
	 * @param right
	 *            the right padding in pixels.
	 * @param bottom
	 *            the bottom padding in pixels.
	 */
	public void setContentPadding(final int left, final int top,
			final int right, final int bottom) {
		ensureViewsSetUp();
		mContentView.setPadding(left, top, right, bottom);
	}

	/**
	 * Rotates the contents of the icon.
	 *
	 * @param degrees
	 *            the amount the contents should be rotated, as a multiple of 90
	 *            degrees.
	 */
	public void setContentRotation(final int degrees) {
		ensureViewsSetUp();
		mRotationLayout.setViewRotation(degrees);
	}

	/**
	 * Sets the child view for the icon.
	 * <p/>
	 * If the view contains a {@link TextView} with the id "text", operations
	 * such as {@link #setTextAppearance} and {@link #makeIcon(String)} will
	 * operate upon that {@link TextView}.
	 */
	public void setContentView(final View contentView) {
		ensureViewsSetUp();
		mRotationLayout.removeAllViews();
		mRotationLayout.addView(contentView);
		mContentView = contentView;
		final View view = mRotationLayout.findViewById(R.id.text);
		mTextView = view instanceof TextView ? (TextView) view : null;
	}

	/**
	 * Rotates the icon.
	 *
	 * @param degrees
	 *            the amount the icon should be rotated, as a multiple of 90
	 *            degrees.
	 */
	public void setRotation(final int degrees) {
		mRotation = ((degrees + 360) % 360) / 90;
	}

	/**
	 * Sets the style of the icon. The style consists of a background and text
	 * appearance.
	 */
	public void setStyle(final int style) {
		setBackground(mContext.getResources().getDrawable(
				IconGenerator.getBackground(style)));
		setTextAppearance(mContext, IconGenerator.getTextStyle(style));
	}

	/**
	 * Sets the text color, size, style, hint color, and highlight color from
	 * the specified <code>TextAppearance</code> resource.
	 *
	 * @param resid
	 *            the identifier of the resource.
	 */
	public void setTextAppearance(final Context context, final int resid) {
		ensureViewsSetUp();
		if (mTextView != null) {
			mTextView.setTextAppearance(context, resid);
		}
	}

	/**
	 * Sets the text color, size, style, hint color, and highlight color from
	 * the specified <code>TextAppearance</code> resource.
	 *
	 * @param resid
	 *            the identifier of the resource.
	 */
	public void setTextAppearance(final int resid) {
		setTextAppearance(mContext, resid);
	}
}
